﻿
Imports std
Imports remotecall

Public Module perfcounter
    Private WithEvents rc As rcserve = Nothing
    Private ReadOnly cpuusage_args As bytesString() = Nothing
    Private timer3guid As Guid = Nothing
    Private pc As map(Of String, map(Of String, map(Of String, PerformanceCounter))) = Nothing
    Private value As map(Of String, map(Of String, map(Of String, Single))) = Nothing

    Private Sub initialArgs(ByRef args() As bytesString, ByVal cat As String, ByVal cun As String, ByVal ins As String)
        ReDim args(2)
        args(0) = cat
        args(1) = cun
        args(2) = ins
    End Sub

    Sub New()
        initialArgs(cpuusage_args, _
                    performanceCounterCategories.processor, _
                    performanceCounterCounters.processorTime, _
                    performanceCounterInstances.total)
    End Sub

    Private Delegate Function traversalCountersDelegate(ByVal p As PerformanceCounter, _
                                                        ByVal cat As String, _
                                                        ByVal cun As String, _
                                                        ByVal ins As String) As Boolean

    Private Sub traversalCounters(ByVal d As traversalCountersDelegate)
        assert(Not d Is Nothing)
        Dim it As map(Of String, map(Of String, map(Of String, PerformanceCounter))).iterator = Nothing
        SyncLock pc
            it = pc.begin()
            While it <> pc.end()
                Dim it2 As map(Of String, map(Of String, PerformanceCounter)).iterator = Nothing
                it2 = (+it).second.begin()
                While it2 <> (+it).second.end()
                    Dim it3 As map(Of String, PerformanceCounter).iterator = Nothing
                    it3 = (+it2).second.begin()
                    While it3 <> (+it2).second.end()
                        If Not d((+it3).second, (+it).first(), (+it2).first(), (+it3).first()) Then
                            raiseError(errorHandle.errorType.warning, "cannot work in traversalCounters")
                        End If
                        it3 += 1
                    End While
                    it2 += 1
                End While
                it += 1
            End While
        End SyncLock
    End Sub

    Private Sub updateCounters()
        traversalCounters(Function(p As PerformanceCounter, cat As String, cun As String, ins As String) As Boolean
                              SyncLock value
                                  value(cat)(cun)(ins) = p.NextValue()
                              End SyncLock
                              Return True
                          End Function)
    End Sub

    Private Function cpuusage(ByVal args() As fromBytes) As bytesString
        Return perfcounter(cpuusage_args)
    End Function

    Private Function perfcounter(ByVal args() As fromBytes) As bytesString
        Dim cat As String = emptyString
        Dim cun As String = emptyString
        Dim ins As String = emptyString
        If Not args Is Nothing AndAlso args.Length() > 0 Then
            cat = convertor.cast(Of bytesString)(args(0))
            If args.Length() > 1 Then
                cun = convertor.cast(Of bytesString)(args(1))
                If args.Length() > 2 Then
                    ins = convertor.cast(Of bytesString)(args(2))
                End If
            End If
        End If
        initialPerfCounter(cat, cun, ins)
        Dim rtn As Single
        SyncLock value
            rtn = value(cat)(cun)(ins)
        End SyncLock
        Return New bytesString(rtn)
    End Function

    Private Sub initialPerfCounter(ByVal cat As String, ByVal cun As String, ByVal ins As String)
        SyncLock pc
            Dim it As map(Of String, PerformanceCounter).iterator = Nothing
            it = pc(cat)(cun).find(ins)
            If it = pc(cat)(cun).end() Then
                raiseError("add performance counter ", cat, "\", cun, "\", ins)
                pc(cat)(cun).insert(ins, New PerformanceCounter(cat, cun, ins))
                pc(cat)(cun)(ins).NextValue()
                sleep(128)
                Dim tries As Int32 = 16
                While tries > 0 AndAlso pc(cat)(cun)(ins).NextValue() = 0
                    sleep(128)
                    tries -= 1
                End While
                SyncLock value
                    value(cat)(cun)(ins) = pc(cat)(cun)(ins).NextValue()
                End SyncLock
            End If
        End SyncLock
    End Sub

    Private Sub initialPerfCounters()
        pc = New map(Of String, map(Of String, map(Of String, PerformanceCounter)))()
        initialPerfCounter(performanceCounterCategories.processor, _
                           performanceCounterCounters.processorTime, _
                           performanceCounterInstances.total)
    End Sub

    Private Function initialRCServe() As Boolean
        rc = New rcserve()
        rc.registerInvoke(Of bytesString)(dpslib.perfcounter.cpuusage, AddressOf cpuusage)
        rc.registerInvoke(Of bytesString)(dpslib.perfcounter.perfcounter, AddressOf perfcounter)
        Return rc.start(dpslib.perfcounter.port)
    End Function

    Private Sub [stop]()
        rc.stop()
        If timer3guid <> Nothing AndAlso timer3guid <> Guid.Empty Then
            timer3.removecall(timer3guid)
            timer3guid = Guid.Empty
        End If
        traversalCounters(Function(p As PerformanceCounter, cat As String, cun As String, ins As String) As Boolean
                              p.Close()
                              Return True
                          End Function)
    End Sub

    Private Sub unhandledException(ByVal ex As Exception)
        [stop]()
    End Sub

    Public Sub main()
        startmain(AddressOf unhandledException, errorHandle.NOLOGFILEMARK)
        counter.initial(counter.NOCOUNTERFILEMARK)
        value = New map(Of String, map(Of String, map(Of String, Single)))()
        initialPerfCounters()
        timer3guid = timer3.addcall(AddressOf updateCounters, secondsToMilliseconds(1))
        If initialRCServe() Then
            While rc.health() AndAlso Not consoleExtSignal()
                sleep()
            End While
            [stop]()
        Else
            raiseError(errorHandle.errorType.warning, "cannot initial rcserve")
        End If
        finishmain()
    End Sub
End Module
